package com.zy.view

import android.animation.Animator
import com.zy.utils.DataBuildUtils.getRandomSortArray
import com.zy.utils.CanvasUtils.buildFirst
import com.zy.utils.CanvasUtils.buildAssistList
import com.zy.utils.CanvasUtils.drawAssistLineV2
import com.zy.zlog.ZLog.d
import android.graphics.DashPathEffect
import android.view.MotionEvent

import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.view.animation.LinearInterpolator
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.view.View
import com.zy.utils.CanvasUtils
import com.zy.utils.data.Point
import com.zy.utils.data.Point.Companion.ASS_INDEX
import com.zy.utils.data.auto.*
import java.lang.NullPointerException
import java.util.ArrayList
import kotlin.math.abs

//拖动
open class DIYSortView : View {

    companion object {
        //编辑模式
        const val MODE_PREVIEW = 0
        const val MODE_EDIT = 1
        const val MODE_MOVE = 2

        //类型
        const val TYPE_CHANGE = 0   //元素交换 ,目标点如果没有这移动过去
        const val TYPE_COVER = 1    //覆盖目标点的位置数据,当前位置如果没有则直接可以创建一个新的
    }

    constructor(context: Context) : super(context) {
        init(context)
    }

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
        init(context)
    }

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context, attrs, defStyleAttr
    ) {
        init(context)
    }

    constructor(
        context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int
    ) : super(context, attrs, defStyleAttr, defStyleRes) {
        init(context)
    }

    var textPaint: Paint? = null
    var indexPaint: Paint? = null
    var assistLinePaint: Paint? = null
    var assistLinePaintV2: Paint? = null
    private fun init(context: Context) {
        textPaint = Paint()
        //设置文本大小
        textPaint!!.textSize = 72f
        textPaint!!.isAntiAlias = true
        //设置文本对齐方式
        textPaint!!.textAlign = Paint.Align.CENTER
        textPaint!!.color = Color.RED

        indexPaint = Paint()
        //设置文本大小
        indexPaint!!.textSize = 36f
        indexPaint!!.style = Paint.Style.STROKE
        indexPaint!!.strokeWidth = 5f
        //设置文本对齐方式
        indexPaint!!.textAlign = Paint.Align.CENTER
        indexPaint!!.color = Color.GRAY

        assistLinePaint = Paint()
        assistLinePaint!!.color = Color.GRAY
        val effects = DashPathEffect(floatArrayOf(20f, 10f, 20f, 10f), 10F)
        assistLinePaint!!.pathEffect = effects

        assistLinePaintV2 = Paint()
        assistLinePaintV2!!.color = Color.BLACK
        assistLinePaintV2!!.strokeWidth = 4f
        assistLinePaintV2!!.pathEffect = effects
    }

    //
    private val pointList: MutableList<Point> = ArrayList()

    //辅助线
    private val assistLiseList: MutableList<PointLine> = ArrayList()

    //预览模式,只可以查看
    private var editMode = MODE_MOVE
    private var D = 100 //辅助格子大小
    private var PER = 45 //每个node大小 一般小于 d/2
    fun setD(d: Int) {
        D = d
        PER = d / 2 - 5
        pointList.forEach {
            it.updateD(d)
        }
    }

    fun setEditMode(mode: Int) {
        this.editMode = mode
    }


    open fun getPointList(): List<Point> {
        return pointList
    }

    fun setPointList(list: List<Point>) {
        pointList.addAll(list)
    }

    /**
     * @param x    x位置
     * @param y    y位置
     * @param size 数据
     */
    fun create(x: Int, y: Int, size: Int, type: Point.Type) {
        val array = getRandomSortArray(size, size)
        pointList.addAll(buildFirst(x, y, D, array, PER, type))
    }

    fun addDiyLayoutNode(point: Point) {
        pointList.clear()
        pointList.add(point)
        invalidate()
    }


    @Deprecated("过时")
    fun create(x: Int, y: Int, array: IntArray?, type: Point.Type) {
        pointList.addAll(buildFirst(x, y, D, array!!, PER, type))
    }

    fun create(x: Int, y: Int, array: IntArray?, circle: Boolean) {
        pointList.addAll(buildFirst(x, y, D, array!!, PER, circle))
    }

    //添加辅助下标
    fun addPoint(x: Int, y: Int, v: String?) {
        val point =
            Point.Builder().setOriginX(x).setOriginY(y).setV(v).setD(D).setPer(PER.toFloat())
                .setIndex(ASS_INDEX).build(
                    Point.Type.SUB_SCRIPT
                )
        pointList.add(point)
    }

    fun addPoint(point: Point) {
        pointList.add(point)
        invalidate()
    }


    //添加辅助元素
    fun addNoMoveAssistList(x: Int, y: Int, array: IntArray?, type: Point.Type) {
        pointList.addAll(buildAssistList(x, y, D, array!!, PER, type))
    }

    fun addNoMoveAssistNode(x: Int, y: Int, v: String?) {
        val point =
            Point.Builder().setOriginX(x).setOriginY(y).setV(v).setD(D).setPer(PER.toFloat())
                .setCanMove(false).setIndex(0).build(Point.Type.ASSIST)
        pointList.add(point)
    }

    private var diyLayoutType = false  //是否是diy布局模式，默认false

    fun setDiyLayoutType() {
        diyLayoutType = true
    }


    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (D > 0) {
            if (diyLayoutType) {
                CanvasUtils.drawAssistLine(canvas, assistLinePaint, D);
            } else {
                drawAssistLineV2(canvas, assistLinePaint, D)
            }
            //辅助线
            for (line in assistLiseList) {
                line.draw(canvas, assistLinePaintV2)
            }
        }

        for (point in pointList) {
            point.draw(canvas, textPaint!!, indexPaint!!)
        }
    }

    //保存node存在的位置
    //二维数组 [0行或者 0列 不保存数据]
    //node移动后 按照就近原则复原
    override fun dispatchTouchEvent(event: MotionEvent): Boolean {
        return super.dispatchTouchEvent(event)
    }

    var touchDownTime: Long = 0
    var hasMove = false //是否移动过

    var diyLayoutListener: DiyLayoutListener? = null

    override fun onTouchEvent(event: MotionEvent): Boolean {
        d("DIYSortView", "onTouchEvent:" + MotionEvent.actionToString(event.action))

        if (editMode == MODE_PREVIEW) {
            return super.onTouchEvent(event)
        }

        if (editMode == MODE_EDIT) {
            return diyLayoutTypeOnTouchEvent(event)
        }

        //mode == MODE_MOVE
        //return super.onTouchEvent(event);
        val x = event.x.toInt()
        val y = event.y.toInt()
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                //找到当前位置的node，记录下来 (有可能没有node 不做任何事)
                curPoint = findMoveNode(event)
                downX = x
                downY = y
                touchDownTime = System.currentTimeMillis()
                hasMove = false
            }
            MotionEvent.ACTION_MOVE ->                 //移动距离阈值
                if (abs(x - downX) > 50 || abs(y - downY) > 50) {
                    //移动node
                    if (curPoint != null) {
                        hasMove = true
                        curPoint!!.updateOff((x - downX).toFloat(), (y - downY).toFloat())
                        invalidate()
                    }
                }
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                //给node找到新的位置
                //新位置已经存在
                //新位置为kong
                //动画
                if (System.currentTimeMillis() - touchDownTime < 100) {
                    if (curPoint != null) {
                        curPoint!!.setMarked()
                        invalidate()
                    }
                }
                if (hasMove) {
                    moveUp(event)
                }
            }
        }
        return true
    }

    private fun diyLayoutTypeOnTouchEvent(event: MotionEvent): Boolean {
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                //找到当前位置的node，记录下来 (有可能没有node 不做任何事)
                curPoint = findMoveNode(event)
                d("DIYSortView", "ACTION_DOWN curPoint:$curPoint")
            }
            MotionEvent.ACTION_MOVE -> {
                //nothing
            }             //移动距离阈值
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                if (curPoint == null) { //当前位置没有node
                    val xy = calculateXY(event)
                    diyLayoutListener?.addNode(xy.first / D, xy.second / D)
                } else {//变更node(删除或者更新)
                    diyLayoutListener?.removeNode(curPoint!!)
                }
                curPoint = null
            }
        }
        return true
    }

    var curPoint: Point? = null
    var downX = 0
    var downY = 0

    private fun findMoveNode(event: MotionEvent): Point? {
        for (point in pointList) {
            if (point.canMove() && point.isRange(event) && point !== curPoint) {
                return point
            }
        }
        return null
    }


    //0 //交换模式
    //1 //覆盖模式 改变数值，自身node的数值不改变，只改变目标node的数值
    var changeType = TYPE_CHANGE


    //计算就XY近位置
    private fun calculateXY(event: MotionEvent): Pair<Int, Int> {
        val curX = event.x.toInt()
        val curY = event.y.toInt()

        //计算就近位置
        var targetX = curX / D * D
        if (curX - targetX > D shr 1) {
            targetX += D
        }
        var targetY = curY / D * D
        if (curY - targetY > D shr 1) {
            targetY += D
        }
        d("DIYSortView", "calculateXY:$curX $targetX $curY $targetY")
        return Pair(targetX, targetY)
    }

    private fun moveUp(event: MotionEvent) {
        curPoint ?: return

        val xy = calculateXY(event)
        //计算就XY近位置
        val targetX = xy.first
        val targetY = xy.second

        val curX = event.x.toInt()
        val curY = event.y.toInt()


        val x = targetX / D
        val y = targetY / D

        if (x == 0 || y == 0) {//移除当前节点(特殊操作)
            removeNode(curPoint)
            return
        }


        val findUpNode = findMoveNode(event)
        d("DIYSortView", "curPoint:$curPoint findUpNode:$findUpNode")

        if (changeType == TYPE_CHANGE) { //默认 交换node
            if (findUpNode != null && curPoint !== findUpNode) {
                //这种情况 node 交换
                changeNode(findUpNode, curPoint!!)
            }
            curPoint!!.updateOff((curX - targetX).toFloat(), (curY - targetY).toFloat())
            curPoint!!.setPercentage(1f)
            curPoint!!.setPoint(targetX / D, targetY / D)
            animatorNode(curPoint!!, 100)
        } else if (changeType == TYPE_COVER) {
            curPoint?.let {
                if (findUpNode != null && it !== findUpNode) { //覆盖目标node的内容
                    findUpNode.updateV(it) //改变目标的值
                    it.resetOff() //自身复位
                    invalidate()
                } else { //如果是添加type，在目标位置添加一个当前node的副本
                    addPoint(it.toBuilder().setOriginX(targetX / D).setOriginY(targetY / D).build())
                    it.resetOff() //自身复位
                    invalidate()
                }

//                {//当目标位置为空时，node 移动到目标位置
//                    it.updateOff((curX - targetX).toFloat(), (curY - targetY).toFloat())
//                    it.setPercentage(1f)
//                    it.setPoint(targetX / D, targetY / D)
//                    animatorNode(it, 100)
//                }
            }
        }
    }

    //findUpNode -> curPoint
    private fun changeNode(findUpNode: Point, curPoint: Point) {
        val targetX = curPoint.originX
        val targetY = curPoint.originY
        val curX = findUpNode.originX
        val curY = findUpNode.originY
        findUpNode.updateOff(curX - targetX, curY - targetY)
        findUpNode.setPercentage(1f)
        findUpNode.setPoint(targetX, targetY)
        findUpNode.exchangeIndex(curPoint)
        animatorNode(findUpNode, 100)
    }

    private fun animatorNode(
        node: Point, duration: Int, listenerAdapter: AnimatorListenerAdapter? = null
    ) {
        animatorNode(listOf(node), duration, listenerAdapter)
    }

    private fun animatorNode(
        nodeS: List<Point>, duration: Int, listenerAdapter: AnimatorListenerAdapter? = null
    ) {
        val mAnimator = ValueAnimator.ofFloat(1f, 0f)
        mAnimator.duration = duration.toLong()
        mAnimator.interpolator = LinearInterpolator()
        mAnimator.addUpdateListener { animation -> //时间流,必须是线性的
            val percentage = animation.animatedValue as Float
            nodeS.forEach {
                it.setPercentage(percentage)
            }
            invalidate()
        }
        mAnimator.addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator, isReverse: Boolean) {
                super.onAnimationEnd(animation, isReverse)
                nodeS.forEach {
                    it.resetOff()
                }
                listenerAdapter?.onAnimationEnd(animation, isReverse)
            }
        })
        mAnimator.start()
    }

    private fun changeNode(
        autoAnimationNodeList: List<AutoAnimationNode>, listenerAdapter: AnimatorListenerAdapter
    ) {
        val nodeList = autoAnimationNodeList.map {
            //node当前位置
            val curX = it.point.originX
            val curY = it.point.originY
            //node目标位置
            val targetX = it.coordinate.x
            val targetY = it.coordinate.y

            it.point.updateOff(curX - targetX, curY - targetY)
            it.point.setPercentage(1f)
            it.point.setPoint(targetX, targetY)

            it.point
        }

        animatorNode(nodeList, 500, listenerAdapter)
    }


    fun autoMove(list: List<AutoNodeStep>) {
        autoMove(list, 0)
    }

    private fun autoMove(list: List<AutoNodeStep>, index: Int) {
        if (index >= list.size) {
            return
        }
        val step = list[index]
        val autoAnimationNodeList = findNodeByStep(step)
        changeNode(autoAnimationNodeList, object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator, isReverse: Boolean) {
                super.onAnimationEnd(animation, isReverse)
                autoMove(list, index + 1)
            }
        })
    }

    public fun findNodeByIndex(index: Int): Point? {
        for (point in pointList) {
            if (point.index == index) {
                return point
            }
        }
        return null
    }

    private fun findNodeByStep(step: AutoNodeStep): List<AutoAnimationNode> {
        val list = mutableListOf<AutoAnimationNode>()

        //TODO优化  存在重复选择node问题 当为AutoVElement时
        pointList.forEach { point ->
            step.list.forEach {
                if (it.findPoint(point)) {
                    list.add(AutoAnimationNode(point, it.coordinate))
                }
            }
        }
        return list
    }

    class NodeLineStep(var indexStart: Int, var indexEnd: Int)

    //辅助线自动动画
    fun autoAddAssistLine(list: List<NodeLineStep>, index: Int = 0) {
        if (index >= list.size) {
            return
        }
        val nodeLineStep = list[index]
        val start = findNodeByIndex(nodeLineStep.indexStart)
        val end = findNodeByIndex(nodeLineStep.indexEnd)
        if (start == null || end == null) {
            throw NullPointerException("autoAddAssistLine Index is illegality")
        }
        val line = PointLine(start.originX, start.originY, end.originX, end.originY, D)
        assistLiseList.add(line)
        invalidate()
        val mAnimator = ValueAnimator.ofFloat(1f, 0f)
        mAnimator.duration = 300
        mAnimator.interpolator = LinearInterpolator()
        mAnimator.addUpdateListener { animation -> //时间流,必须是线性的
            val percentage = animation.animatedValue as Float
        }
        mAnimator.addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator, isReverse: Boolean) {
                super.onAnimationEnd(animation, isReverse)
                autoAddAssistLine(list, index + 1)
            }
        })
        mAnimator.start()
    }

    private fun findNodeByV(v: String): List<Point> {
        return pointList.filter {
            it.v == v
        }
    }

    //相同数据一起动画
    fun autoSortByVTogether(x: Int, y: Int, intRange: IntRange) {
        var curIndexX = 0
        val list: MutableList<AutoNodeStep> = ArrayList<AutoNodeStep>()
        for (v in intRange) {
            val autoVElementS = findNodeByV(v.toString()).map {
                val element = AutoIndexElement(it.index, Coordinate(x + curIndexX, y))
                curIndexX++
                element
            }
            if (autoVElementS.isNotEmpty()) {
                list.add(AutoNodeStep(autoVElementS))
            }
        }

        autoMove(list)
    }


    //相同数据按照index step by step
    fun autoSortByV(x: Int, y: Int, intRange: IntRange) {
        var curIndexX = 0
        val list: MutableList<AutoNodeStep> = ArrayList<AutoNodeStep>()

        var baseAssBaseX = 2 //辅助下标
        for (v in intRange) {
            findNodeByV(v.toString()).forEach {
                val element = AutoIndexElement(it.index, Coordinate(x + curIndexX, y))
                val assElement = AutoIndexElement(ASS_INDEX, Coordinate(++baseAssBaseX, 2))
                curIndexX++

                list.add(AutoNodeStep(listOf(element, assElement)))
            }

        }

        autoMove(list)
    }

    fun removeNode(point: Point?) {
        point ?: return
        pointList.remove(point)
        invalidate()
    }

    interface DiyLayoutListener {
        fun addNode(x: Int, y: Int)

        fun removeNode(point: Point)
    }
}